package es.ubu.ceudaaj.exercise.ud02.t04.l03;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.GregorianCalendar;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;

import es.ubu.ceudaaj.exercise.ud02.t04.l03.model.Contactos;
import es.ubu.ceudaaj.exercise.ud02.t04.l03.model.Dataroot;

/**
 * Class representing a list of contacts that can be read and written into and
 * from an xml file.
 * 
 * This version uses JAXB to process the xml.
 * 
 */
public class AgendaJAXB implements Agenda {

	private static final String XML_INPUT_PATH = "src/main/resources/xml/Contactos.xml";
	private static final String XML_OUTPUT_PATH = "src/main/resources/xml/Contactos_new.xml";

	// Path to the JAXB classes (that have been generated first, by using 'jxc')
	private static final String JAXB_PACKAGE = "es.ubu.ceudaaj.exercise.ud02.t04.l03.model";

	// Attribute to keep a reference to the current XML in memory.
	private Dataroot dataroot = null;

	private static AgendaJAXB instance = null;

	/**
	 * Private constructor This class should only be used as singleton
	 */
	private AgendaJAXB() {
	}

	/**
	 * Returns a JAXB based agenda
	 * 
	 * @return AgendaJAXB
	 */
	public static AgendaJAXB getInstance() {
		if (instance == null)
			instance = new AgendaJAXB();
		return instance;
	}

	/**
	 * Shows a list of current contacts on console. (Only name and surnames).
	 */
	public ArrayList<GUIDataModel> listContacts() {
		ArrayList<GUIDataModel> gdm = new ArrayList<GUIDataModel>();
		if (dataroot != null && !dataroot.getContactos().isEmpty()) {
			for (Contactos contact : dataroot.getContactos()) {
				gdm.add(toGUIDataModel(contact));
			}
		} else {
			System.out.println("(lista vacia)");
		}
		return gdm;
	}

	/**
	 * Prints detailed information of selected user on console.
	 * 
	 * @param index
	 *            Position of selected contact.
	 * @throws IOException
	 */
	public GUIDataModel printContactDetail(int index) throws IOException {
		if (dataroot != null && dataroot.getContactos() != null
				&& dataroot.getContactos().size() >= index && index > 0) {
			Contactos cont = dataroot.getContactos().get(index - 1);
			return toGUIDataModel(cont);
		} else {
			System.out.println("No existe en la lista de contactos.");
			return null;
		}
	}

	/**
	 * Creates a new contact and adds it to the XML representation in memory.
	 * 
	 * @throws IOException
	 */
	public void createNewContact(GUIDataModel gdm) throws IOException {

		if (dataroot == null) {
			dataroot = new Dataroot();
			GregorianCalendar c = new GregorianCalendar();
			c.setTime(new Date());
			XMLGregorianCalendar xmlgc;
			try {
				xmlgc = DatatypeFactory.newInstance()
						.newXMLGregorianCalendar(c);
				dataroot.setGenerated(xmlgc);
			} catch (DatatypeConfigurationException e) {
				System.err.println("Error al crear nuevo contacto: " + e);
				return;
			}
		}

		Contactos contacto = new Contactos();
		contacto.setIdContacto(dataroot.getContactos().size() + 1);
		contacto.setNombre(gdm.nombre);
		contacto.setApellidos(gdm.apellidos);
		contacto.setTelefonoTrabajo(gdm.telefono);
		contacto.setNomCorreoElectronico(gdm.email);
		contacto.setCargo(gdm.cargo);
		contacto.setCodPostal(gdm.codPostal);

		dataroot.getContactos().add(contacto);
	}

	/**
	 * Method for editing the information for the selected contact in the XML
	 * representation in memory.
	 * 
	 * @param index
	 *            Position of selected contact.
	 * @throws IOException
	 */
	public boolean editContact(GUIDataModel gdm) throws IOException {
		int index = Integer.parseInt(gdm.id);
		if (dataroot != null && dataroot.getContactos() != null
				&& dataroot.getContactos().size() >= index && index > 0) {

			Contactos contacto = dataroot.getContactos().get(index - 1);
			contacto.setNombre(gdm.nombre);
			contacto.setApellidos(gdm.apellidos);
			contacto.setTelefonoTrabajo(gdm.telefono);
			contacto.setNomCorreoElectronico(gdm.email);
			contacto.setCargo(gdm.cargo);
			contacto.setCodPostal(gdm.codPostal);
			return true;
		} else {
			System.out.println("No existe en la lista de contactos.");
			return false;
		}
	}

	/**
	 * Removes the selected contact from the XML representation in memory.
	 * 
	 * @param index
	 *            Position of selected contact.
	 */
	public void deleteContact(int index) {
		if (dataroot != null && dataroot.getContactos() != null
				&& dataroot.getContactos().size() >= index) {
			dataroot.getContactos().remove(index - 1);
			System.out.println("Contacto borrado.");
		} else {
			System.out.println("No existe en la lista de contactos.");
		}
	}

	/**
	 * Writes the current XML in a file.
	 * 
	 * @throws JAXBException
	 */
	public boolean saveXML() {
		if (dataroot != null) {
			JAXBContext jc;
			try {
				jc = JAXBContext.newInstance(JAXB_PACKAGE);
				Marshaller marshaller = jc.createMarshaller();
				marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
				marshaller.marshal(dataroot, new File(XML_OUTPUT_PATH));
				System.out.println("Se ha guardado correctamente el fichero "
						+ XML_OUTPUT_PATH);
				return true;
			} catch (JAXBException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} else {
			System.out.println("Aun no existe ningun contacto");
		}

		return false;
	}

	/**
	 * Reads the input XML file.
	 */
	public void loadXML() {
		try {
			JAXBContext jc = JAXBContext.newInstance(JAXB_PACKAGE);
			Unmarshaller unmarshaller = jc.createUnmarshaller();
			dataroot = (Dataroot) unmarshaller.unmarshal(new File(
					XML_INPUT_PATH));
			System.out.println("Se ha leido el fichero " + XML_INPUT_PATH);
		} catch (JAXBException ex) {
			System.err.println("Error al obtener los contactos del XML: " + ex);
		}
	}

	public ArrayList<GUIDataModel> searchByName(String name) {
		ArrayList<GUIDataModel> results = new ArrayList<GUIDataModel>();
		for (Contactos contact : dataroot.getContactos()) {
			if (contact.getNombre().equals(name)) {
				results.add(toGUIDataModel(contact));
			}
		}
		return results;
	}

	public ArrayList<GUIDataModel> searchByJob(String job) {
		ArrayList<GUIDataModel> results = new ArrayList<GUIDataModel>();
		for (Contactos contact : dataroot.getContactos()) {
			if (contact.getCargo().equals(job)) {
				results.add(toGUIDataModel(contact));
			}
		}
		return results;
	}

	public ArrayList<GUIDataModel> searchByPostalCode(String postalCode) {
		ArrayList<GUIDataModel> results = new ArrayList<GUIDataModel>();
		for (Contactos contact : dataroot.getContactos()) {
			if (contact.getCodPostal().equals(postalCode)) {
				results.add(toGUIDataModel(contact));
			}
		}
		return results;
	}

	/**
	 * Converts data to GUIDataModel
	 */
	public GUIDataModel toGUIDataModel(Contactos cont) {
		GUIDataModel gdm = new GUIDataModel();
		gdm.setId(String.valueOf(cont.getIdContacto()));
		gdm.setNombre(cont.getNombre());
		gdm.setApellidos(cont.getApellidos());
		gdm.setCargo(cont.getCargo());
		gdm.setTelefono(cont.getTelefonoTrabajo());
		gdm.setEmail(cont.getNomCorreoElectronico());
		gdm.setCodPostal(cont.getCodPostal());
		gdm.id = String.valueOf(cont.getIdContacto());
		return gdm;

	}
	
	/**
	 * Returns a new instance with no XML loaded
	 * FOR TESTING PURPOSES ONLY
	 * @return
	 */
	public static AgendaJAXB getEmptyInstance(){
		instance = new AgendaJAXB();
		return instance;
	}

}